{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Repair Problem\n",
    "Ross, Simulation 5th edition, Section 7.7, p. 124-126"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Description\n",
    "\n",
    "A system needs $n$ working machines to be operational. To guard against machine breakdown, additional machines are kept available as spares. Whenever a machine breaks down it is immediately replaced by a spare and is itself sent to the repair facility, which consists of a single repairperson who repairs failed machines one at a time. Once a failed machine has been repaired it becomes available as a spare to be used when the need arises. All repair times are independent random variables having the common distribution function $G$. Each time a machine is put into use the amount of time it functions before breaking down is a random variable, independent of the past, having distribution function $F$.\n",
    "\n",
    "The system is said to “crash” when a machine fails and no spares are available. Assuming that there are initially $n + s$ functional machines of which $n$ are put in use and $s$ are kept as spares, we are interested in simulating this system so as to approximate $E[T]$, where $T$ is the time at which the system crashes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "Pkg.update()\n",
    "Pkg.add(\"Distributions\")\n",
    "Pkg.add(\"SimJulia\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use Distributions.jl and SimJulia.jl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "using Distributions\n",
    "using ResumableFunctions\n",
    "using SimJulia"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define constants"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "const RUNS = 5\n",
    "const N = 10\n",
    "const S = 3\n",
    "const SEED = 150\n",
    "const LAMBDA = 100\n",
    "const MU = 1\n",
    "\n",
    "srand(SEED)\n",
    "const F = Exponential(LAMBDA)\n",
    "const G = Exponential(MU);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define the behaviour of a machine"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "machine (generic function with 1 method)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "@resumable function machine(sim::Simulation, repair_facility::Resource, spares::Store{Process})\n",
    "    while true\n",
    "        try\n",
    "            @yield timeout(sim, Inf)\n",
    "        catch exc\n",
    "        end\n",
    "        #println(\"At time $(now(sim)): $(active_process(sim)) starts working.\")\n",
    "        @yield timeout(sim, rand(F))\n",
    "        #println(\"At time $(now(sim)): $(active_process(sim)) stops working.\")\n",
    "        get_spare = get(spares)\n",
    "        @yield get_spare | timeout(sim, 0.0)\n",
    "        if state(get_spare) != SimJulia.idle\n",
    "            interrupt(value(get_spare))\n",
    "        else\n",
    "            throw(SimJulia.StopSimulation(\"No more spares!\"))\n",
    "        end\n",
    "        @yield request(repair_facility)\n",
    "        #println(\"At time $(now(sim)): $(active_process(sim)) repair starts.\")\n",
    "        @yield timeout(sim, rand(G))\n",
    "        @yield release(repair_facility)\n",
    "        #println(\"At time $(now(sim)): $(active_process(sim)) is repaired.\")\n",
    "        @yield put(spares, active_process(sim))\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Startup procedure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "start_sim (generic function with 1 method)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "@resumable function start_sim(sim::Simulation, repair_facility::Resource, spares::Store{Process})\n",
    "    procs = Process[]\n",
    "    for i=1:N\n",
    "        push!(procs, @process machine(sim, repair_facility, spares))\n",
    "    end\n",
    "    @yield timeout(sim, 0.0)\n",
    "    for proc in procs\n",
    "        interrupt(proc)\n",
    "    end\n",
    "    for i=1:S\n",
    "        @yield put(spares, @process machine(sim, repair_facility, spares))\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## One simulation run"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sim_repair (generic function with 1 method)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function sim_repair()\n",
    "    sim = Simulation()\n",
    "    repair_facility = Resource(sim)\n",
    "    spares = Store{Process}(sim)\n",
    "    @process start_sim(sim, repair_facility, spares)\n",
    "    msg = run(sim)\n",
    "    stop_time = now(sim)\n",
    "    println(\"At time $stop_time: $msg\")\n",
    "    stop_time\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Multiple simulations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "At time 5573.772841846017: No more spares!\n",
      "At time 1438.0294516073466: No more spares!\n",
      "At time 7077.413276961621: No more spares!\n",
      "At time 7286.490682742159: No more spares!\n",
      "At time 6820.788098062124: No more spares!\n",
      "Average crash time: 5639.298870243853\n"
     ]
    }
   ],
   "source": [
    "results = Float64[]\n",
    "for i=1:RUNS\n",
    "    push!(results, sim_repair())\n",
    "end\n",
    "println(\"Average crash time: \", sum(results)/RUNS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 0.6.0",
   "language": "julia",
   "name": "julia-0.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "0.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
